//*************************************************************************************
// EGTB Validator Template Header File
//*************************************************************************************

#pragma once

#include "Validator.h"

namespace EndGameDB
{

	// Validates one SubDatabase slice
	//////////////////////////////////////////////////////////////////////////////////////////////////

	template<typename BB, int MaxField, int BitsPerRow, int MaxPieces, int MaxRank, typename DB_READER>
	void ValidateSubDbSlice(int wm, int wk, int bm, int bk, int wmrank, int bmrank, FILE* fLog, DB_READER* pReader)
	{
		// get the subdatabase slice size
		int64 size = GetDatabaseSize<BitsPerRow, MaxField>(wm, wk, bm, bk, wmrank, bmrank);
		int64 index = 0;

		// report progress of validated subdatabase slice
		if(OnValidationSubDbStartedPtr != NULL)
			OnValidationSubDbStartedPtr(wm, wk, bm, bk, wmrank, bmrank);

		fprintf(fLog, "wm: %d, wk: %d, bm: %d, bk: %d, wmrank: %d, bmrank: %d\n", 
								wm, wk, bm, bk, wmrank, bmrank);
		fprintf(fLog, "-----------------------------------------------------\n"); 


		// for both color sides
		for(int color = 0; color < 2; color++)
		{
			fprintf(fLog, "STM: %s\n", ColorToStr(color));

			// iterate through all BitBoard positions
			for(index = 0; index < size; index++)
			{
				// initiaze default values of results and DTM
				int resultValue, DTM, opponentResult;
				ClearResultValues(resultValue, DTM);
				opponentResult = LOSS;

				// convert index to bitboard
				BB p;
				IndexToBitBoardXX<BB>(index, &p, wm, wk, bm, bk, wmrank, bmrank);

				// check impossible BitBoard position
				if(p.bm & p.wm)
					continue;

				// test if capture, we ignore captures, they are not searched in the engine
				// if capture BitBoard, continue
				if(TestJump<BB>(g_ED, &p, color == WHITE))
					continue;

				// increment the number of positions
				validationPositions++;				

				// lookup the current value of bitboard position situation
				uint8 curMoveDTM;
				int curMoveResult = pReader->Lookup<BB, BitsPerRow, MaxField>(&p, color, &curMoveDTM);

				// generate all possible moves
				BB bbMoves[MAX_MOVES];
				memset(bbMoves, 0, sizeof(BB) * MAX_MOVES);
				int moveCount = MoveGenerator<BB>(g_ED, &p, color == WHITE ? true : false, bbMoves);

				// if no move, DTM is zero
				if(moveCount == 0)
					DTM = 0;
				
				BB nextPosition;
				memset(&nextPosition, 0, sizeof(BB));

				// iterate among all generated moves
				for(int i = 0; i < moveCount; i++)
				{
					// do the move
					ToggleMove<BB>(&p, bbMoves + i);

					// lookup the EGTB values for the new position
					uint8 tmpMoveDTM, dummy;
					int tmpMoveResult = pReader->Lookup<BB, BitsPerRow, MaxField>(&p, color^1, &tmpMoveDTM);

					// compare the results
					if(CompareResults(resultValue, DTM, tmpMoveResult, tmpMoveDTM))
					{
						opponentResult = pReader->Lookup<BB, BitsPerRow, MaxField>(&p, color^1, &dummy);
						nextPosition = p;			
					}

					// undo move
					ToggleMove<BB>(&p, bbMoves + i);
				} // for - move iteration

				maxDTM = max(maxDTM, DTM);

				// if results not the same, count errors
				if(resultValue != curMoveResult)
				{
					validationWDL_Errors++;
					fprintf(fLog, "Incorrect result value - OrigValue: %s, NextValue: %s\n", 
								ResultToStr(curMoveResult), ResultToStr(resultValue));
					fprintf(fLog, "Opponent succ value: %s\n", ResultToStr(opponentResult));
					fprintf(fLog, "Current Position:\n");
					fprintf(fLog, "----------------:\n");
					LogBB_FP(fLog, p);
					fprintf(fLog, "Next Position:\n");
					fprintf(fLog, "----------------:\n");
					LogBB_FP(fLog, nextPosition);
				}
				else
				{
					// if result values correct, check DTM, if not DRAW
					if(curMoveResult != UNKNOWN && curMoveResult != DRAW)
					{
						if(curMoveDTM != DTM + 1 /*|| DTM >= 50*/)
						{
							validationDTM_Errors++;
							fprintf(fLog, "Incorrect DTM value - Pos: %s, OrigDTM: %d, NextDTM: %d\n", 
								ResultToStr(curMoveResult), curMoveDTM, DTM);
							fprintf(fLog, "Current Position:\n");
							fprintf(fLog, "----------------:\n");
							LogBB_FP(fLog, p);
							fprintf(fLog, "Next Position:\n");
							fprintf(fLog, "----------------:\n");
							LogBB_FP(fLog, nextPosition);
						}
					}
				}

			} // for - index to bitboard iteration
		} // for - side to move iteration
	}


	// Validates the subdatabase
	//////////////////////////////////////////////////////////////////////

	template<typename BB, int MaxRank, int MaxField, int BitsPerRow, int MaxPieces, typename DB_READER>
	int ValidateSubDb(int wm,int wk,int bm,int bk, FILE* fLog, DB_READER* pReader)
	{
		int wmrank, bmrank; // maximal rank of the white and black men
		int n = bm+bk+wm+wk;

		// is this a valid database?
		// one side has no pieces:
		if((bm+bk==0) || (wm+wk==0)) return 0;


		// white has more pieces than black
		if(wm+wk<bm+bk)
			return 0;

		// absurd databases: if maxpieces = 8, we don't do 5-3, 6-2, 7-1 or 6-1 or 5-1 etc.
		// this depends on MAXPIECE of course!
		if(wm+wk>MAXPIECE)
			return 0;

		// more bk than wk in case of equal number of pieces
		if(wk+wm==bk+bm)
		{
			if(bk>wk)
				return 0;
		}

		if(bm==0 && wm==0)
			ValidateSubDbSlice<BB, MaxField, BitsPerRow, MaxPieces, MaxRank, DB_READER>(wm,wk,bm,bk,0,0, fLog, pReader);
	
		if(wm!=0 && bm==0)
		{
			for(wmrank=MaxRank-1;wmrank>=((wm-1)/BitsPerRow);wmrank--)
				ValidateSubDbSlice<BB, MaxField, BitsPerRow, MaxPieces, MaxRank, DB_READER>(wm,wk,bm,bk,wmrank,0, fLog, pReader);
		}
	
		if(bm!=0 && wm==0)
		{
			for(bmrank=MaxRank-1;bmrank>=((bm-1)/BitsPerRow);bmrank--)
				ValidateSubDbSlice<BB, MaxField, BitsPerRow, MaxPieces, MaxRank, DB_READER>(wm,wk,bm,bk,0,bmrank, fLog, pReader);
		}

		if(wm!=0 && bm!=0)
		{
			for(wmrank=MaxRank-1;wmrank>=((wm-1)/BitsPerRow);wmrank--)
			{
				for(bmrank=MaxRank-1;bmrank>=((bm-1)/BitsPerRow);bmrank--)
					ValidateSubDbSlice<BB, MaxField, BitsPerRow, MaxPieces, MaxRank, DB_READER>(wm,wk,bm,bk,wmrank,bmrank, fLog, pReader);
			}
		}

		return 1;
	}


	// Validates the correctness of one database
	//////////////////////////////////////////////////////////////////////

	template<typename BB, int MaxRank, int MaxField, int BitsPerRow, int MaxPieces, typename DB_READER>
	int ValidateDb(int n, FILE* fLog, DB_READER* pReader)
	{

		int bm=0,bk=0,wm=0,wk=0;
		int div1, div2, div3;

		for(div1=0;div1<=n;div1++)
		{
			for(div2=div1;div2<=n;div2++)
			{
				for(div3=div2;div3<=n;div3++)
				{
					wm=div1;
					wk=div2-div1;
					bm=div3-div2;
					bk=n-div3;
					ValidateSubDb<BB, MaxRank, MaxField, BitsPerRow, MaxPieces, DB_READER>(wm,wk,bm,bk, fLog, pReader);
				}
			}
		}

		return 1;
	}


	// The main API function for validation of EGTB
	//////////////////////////////////////////////////////////////////////

	template<typename BB, int MaxRank, int MaxField, int BitsPerRow, int MaxPieces>
	void ValidateDatabase(eCheckersType checkersType, const wchar_t* lpDir)
	{
		// TO-DO: refactor the logging file name
		FILE* fLog = NULL;
		_wfopen_s(&fLog, L"c:\\Log\\Validation.log", L"w");
		validationWDL_Errors = 0;
		validationDTM_Errors = 0;
		validationPositions = 0;
		maxDTM = 0;

		if(fLog)
		{
			// allocate the global movegen configuration
			g_ED = new SEngineData();
			memset(g_ED, 0, sizeof(SEngineData));

			// set bitboard generators
			SetCheckersTypeMoveGen(g_ED, checkersType);

			// create reader
			DBREADER* pReader = new CDbReader<MaxPieces, MaxPieces, MaxRank>();
			pReader->Initialize(200, g_ED, lpDir);

			// do verification itself
			for(int n=2; n<=MaxPieces; n++)
			{
				ValidateDb<BB, MaxRank, MaxField, BitsPerRow, MaxPieces, CDbReader<MaxPieces, MaxPieces, MaxRank> >(n, fLog, pReader);
			}

			// clean up
			delete pReader;
			pReader = NULL;

			delete g_ED;
			g_ED = NULL;
		} // if

		fprintf(fLog, "WDL Error count: %d\n", validationWDL_Errors);
		fprintf(fLog, "DTM Error count: %d\n", validationDTM_Errors);
		fprintf(fLog, "Maximum DTM reached: %d\n", maxDTM);
	
		fprintf(fLog, "Total position count: %d", validationPositions);
		fclose(fLog);
	}









}
